package com.cjx.stars.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cjx.stars.common.MyException;
import com.cjx.stars.common.Response;
import com.cjx.stars.common.ResponseUtils;
import com.cjx.stars.constant.MessageTypeEnum;
import com.cjx.stars.mapper.BlogCommentsMapper;
import com.cjx.stars.mapper.CommentLikeMapper;
import com.cjx.stars.pojo.table.*;
import com.cjx.stars.pojo.request.AddCommentRequest;
import com.cjx.stars.pojo.vo.UserVo;
import com.cjx.stars.service.*;
import com.cjx.stars.utils.UserHolder;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import java.util.ArrayList;
import java.util.List;

import static com.cjx.stars.constant.FailMessage.*;
import static com.cjx.stars.constant.RedisConstant.COMMENT_LIKED_KEY;
import static com.cjx.stars.constant.RedisConstant.MESSAGE_LIKE_NUM_KEY;
import static com.cjx.stars.constant.SystemConstants.PAGE_SIZE;

/**
 * @author cjx
 * @description 针对表【blog_comments】的数据库操作Service实现
 * @createDate 2024-04-04 14:44:34
 */
@Service
public class BlogCommentsServiceImpl extends ServiceImpl<BlogCommentsMapper, BlogComments>
        implements BlogCommentsService {


    @Resource
    @Lazy
    private BlogService blogService;
    @Resource
    private UserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private CommentLikeService commentLikeService;
    @Resource
    private CommentLikeMapper commentLikeMapper;
    @Resource
    private BlogCommentsMapper blogCommentsMapper;
    @Resource
    private MessageService messageService;

    @Override
    public Response<?> addComment(AddCommentRequest commentRequest) {
        //判断是否存在此博文
        Long blogId = commentRequest.getBlogId();
        if (blogId == null || blogId <= 0) {
            throw new MyException(PARAMETER_ABNORMAL);
        }
        Blog blog = blogService.getById(blogId);
        if (blog == null) {
            throw new MyException(NOT_THE_BLOG);
        }
        //判断评论内容是否存在
        String content = commentRequest.getContent();
        if (StrUtil.isBlank(content)) {
            throw new MyException(PARAMETER_ABNORMAL);
        }
        //设置各类信息给评论
        BlogComments blogComments = new BlogComments();
        blogComments.setBlogId(blogId);
        blogComments.setContent(content);
        //设置创建用户id
        blogComments.setUserId(UserHolder.getUser().getUserId());
        boolean save = save(blogComments);
        if (BooleanUtil.isFalse(save)) {
            throw new MyException(COMMENT_FAILED);
        }
        //给该博文的评论数+1
        blogService.addCommentNum(blogId);
        return ResponseUtils.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response<?> getCommentList(Long bolgId) {
        //1.根据博文Id查询评论列表
        List<BlogComments> list = list(new LambdaQueryWrapper<BlogComments>().eq(BlogComments::getBlogId, bolgId));
        insertCommentsUser(list);
        return ResponseUtils.success(list);
    }

    /**
     * 根据评论信息插入对应的用户信息
     *
     * @param list 评论信息列表
     */
    private void insertCommentsUser(List<BlogComments> list) {
        //1.获取每条评论的评论用户Id
        List<Long> userIds = list.stream().map(bolgComment -> Long.valueOf(bolgComment.getUserId())).toList();
        //2.根据用户Id获取用户信息
        List<User> users = new ArrayList<>();
        for (Long userId : userIds) {
            users.add(userService.getById(userId));
        }
        //3.User类包装成UserVo类
        List<UserVo> userVos = users.stream().map(user -> BeanUtil.copyProperties(user, UserVo.class)).toList();
        //3.1将用户信息赋值给对应的评论类
        for (int i = 0; i < users.size(); i++) {
            list.get(i).setCommentUser(userVos.get(i));
            //3.2增添当前用户是否给该评论点赞属性
            boolean isLike = isLike(list.get(i).getId(), Long.valueOf(UserHolder.getUser().getUserId()));
            list.get(i).setIsLiked(isLike);
        }
    }

    /**
     * 判断当前用户是否给此评论点赞
     *
     * @param id    评论id
     * @param aLong 用户id
     * @return 是否点赞
     */
    private boolean isLike(Long id, Long aLong) {
        return commentLikeService.count(new LambdaQueryWrapper<CommentLike>().eq(CommentLike::getCommentId, id)
                .eq(CommentLike::getUserId, aLong)) > 0;
    }

    @Override
    @Transactional
    public Response<?> addCommentLike(Long commentId) {
        //判断此评论是否存在
        BlogComments comment = getById(commentId);
        if (comment == null) {
            ResponseUtils.fail(THE_COMMENT_NOT);
        }
        // 1.获取登录用户
        Long userId = Long.valueOf(UserHolder.getUser().getUserId());
        // 2.判断当前登录用户是否已经点赞
        String key = COMMENT_LIKED_KEY + commentId;
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        if (BooleanUtil.isFalse(isMember)) {
            //3.如果未点赞，可以点赞
            //3.1 数据库点赞数+1
            boolean isSuccess = update().setSql("liked_num = liked_num + 1").eq("id", commentId).update();
            //保存数据到博文点赞关系表
            //判断之前是否有点赞，如果有点赞记录，则恢复点赞记录
            CommentLike oldCommentLike = commentLikeMapper.getIsLike(commentId, userId);
            if (oldCommentLike != null) {
                oldCommentLike.setIsDelete(0);
                oldCommentLike.setUpdateTime(DateTime.now());
                commentLikeMapper.updateDeleteLike(commentId, userId);
            } else {
                //没有过点赞记录，创建一个
                CommentLike commentLike = new CommentLike();
                commentLike.setCommentId(commentId);
                commentLike.setUserId(userId);
                boolean save = commentLikeService.save(commentLike);
                if (BooleanUtil.isFalse(save) || BooleanUtil.isFalse(isSuccess)) {
                    throw new MyException(FAILED_TO_LIKE);
                }
                //3.2 保存用户到Redis的set集合

            }
            if (isSuccess) {
                stringRedisTemplate.opsForSet().add(key, userId.toString());
            }
            //存入点赞消息信息，将点赞对象的点赞数量+1
            Message message = new Message();
            message.setType(MessageTypeEnum.BLOG_COMMENT_LIKE.getValue());
            message.setFromId(userId);
            message.setToId(Long.valueOf(comment.getUserId()));
            message.setData(String.valueOf(comment.getId()));
            messageService.save(message);
            //查询点赞数量redis信息并做处理
            String likeNumKey = MESSAGE_LIKE_NUM_KEY + comment.getUserId();
            Boolean hasKey = stringRedisTemplate.hasKey(likeNumKey);
            if (Boolean.TRUE.equals(hasKey)) {
                stringRedisTemplate.opsForValue().increment(likeNumKey);
            } else {
                stringRedisTemplate.opsForValue().set(likeNumKey, "1");
            }
        } else {
            //4.如果已点赞，取消点赞
            //4.1 数据库点赞数-1
            boolean isSuccess = update().setSql("liked_num = liked_num - 1").eq("id", commentId).update();
            //删除该点赞关系
            commentLikeService.remove(new LambdaQueryWrapper<CommentLike>().eq(CommentLike::getCommentId, commentId)
                    .eq(CommentLike::getUserId, userId));
            //4.2 把用户从Redis的set集合移除
            if (isSuccess) {
                stringRedisTemplate.opsForSet().remove(key, userId.toString());
            }
            //删除点赞消息信息
            messageService.remove(new LambdaQueryWrapper<Message>()
                    .eq(Message::getType, MessageTypeEnum.BLOG_COMMENT_LIKE.getValue())
                    .eq(Message::getFromId, userId)
                    .eq(Message::getToId, comment.getUserId())
                    .eq(Message::getData, String.valueOf(comment.getId())));
            //被点赞用户的消息数量-1
            String likeNumKey = MESSAGE_LIKE_NUM_KEY + comment.getUserId();
            String upNumStr = stringRedisTemplate.opsForValue().get(likeNumKey);
            if (!StrUtil.isNullOrUndefined(upNumStr) && Long.parseLong(upNumStr) != 0) {
                stringRedisTemplate.opsForValue().decrement(likeNumKey);
            }
        }
        return ResponseUtils.success();
    }

    @Override
    public BlogComments getCommentById(Long commentId) {
        BlogComments comment = getById(commentId);
        if (comment == null) {
            throw new MyException(THE_COMMENT_NOT);
        }
        comment.setIsLiked(isLike(commentId, Long.valueOf(UserHolder.getUser().getUserId())));
        return comment;
    }

    @Override
    @Transactional
    public Response<?> deleteById(Long commentId) {
        if (commentId <= 0) {
            throw new MyException(PARAMETER_ABNORMAL);
        }
        //使该评论博文评论数-1
        BlogComments comment = getById(commentId);
        if (comment == null) {
            throw new MyException(THE_COMMENT_NOT);
        }
        Long blogId = comment.getBlogId();
        blogService.update().setSql("comments_num = comments_num - 1").eq("id", blogId).update();
        //删除该评论
        boolean remove = remove(new LambdaQueryWrapper<BlogComments>().eq(BlogComments::getId, commentId));
        if (BooleanUtil.isFalse(remove)) {
            throw new MyException(FAILED_TO_DELETE_COMMENT);
        }
        return ResponseUtils.success();

    }

    @Override
    public Response<?> getMyCommentList(int currentPage) {
        //1.分页查询我的评论
        LambdaQueryWrapper<BlogComments> wrapper = new LambdaQueryWrapper<BlogComments>()
                .eq(BlogComments::getUserId, Long.valueOf(UserHolder.getUser().getUserId()));
        Page<BlogComments> commentsPage = selectCommentPage(currentPage, wrapper);
        //2.评论量不为空则查询评论中的博文数据
        if (commentsPage.getSize() == 0) {
            return ResponseUtils.success();
        }
        List<BlogComments> blogCommentsList = commentsPage.getRecords();
        for (BlogComments comments : blogCommentsList) {
            //2.1.将博文数据设置入对应的评论信息
            comments.setBlog(blogService.getBlogVoById(comments.getBlogId()));
        }
        //3.插入评论用户信息
        insertCommentsUser(blogCommentsList);
        //4.返回评论信息
        return ResponseUtils.success(commentsPage);
    }

    /**
     * 条件分页查询评论信息
     *
     * @param currentPage 分页参数
     * @param wrapper     查询条件
     * @return 评论信息
     */
    private Page<BlogComments> selectCommentPage(int currentPage, LambdaQueryWrapper<BlogComments> wrapper) {
        Page<BlogComments> page = new Page<>(currentPage, PAGE_SIZE);
        return blogCommentsMapper.selectPage(page, wrapper);
    }


}




